package com.axonactive.yolo.controller;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.axonactive.yolo.domain.ExchangeRate;
import com.axonactive.yolo.domain.SubscribedFlight;
import com.axonactive.yolo.domain.Task;
import com.axonactive.yolo.domain.TaskUserId;
import com.axonactive.yolo.dto.AirlinesFilter;
import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dto.Parameters;
import com.axonactive.yolo.dto.ParametersFilter;
import com.axonactive.yolo.dto.TaskDTO;
import com.axonactive.yolo.service.interfaces.AirlineService;
import com.axonactive.yolo.service.interfaces.CountryService;
import com.axonactive.yolo.service.interfaces.ExchangeRateService;
import com.axonactive.yolo.service.interfaces.SubscribedFlightService;
import com.axonactive.yolo.service.interfaces.TaskService;
import com.axonactive.yolo.service.interfaces.TaskUserIdService;
import com.axonactive.yolo.service.internal.FlightFactoryService;
import com.axonactive.yolo.service.internal.FlightRequesterService;
import com.axonactive.yolo.util.CONSTANTS;
import com.axonactive.yolo.util.IOUtil;
import com.axonactive.yolo.util.Utils;
import com.axonactive.yolo.util.session.SessionHelper;
import com.axonactive.yolo.validation.ParamValidator;

@Controller
@RequestMapping(value = "/flight")
@PropertySource("classpath:messages.properties")
public class FlightController {
	private static final String[] cabinClasses = { "Economy", "Business", "All" };
	private static final Logger LOGGER = Logger
			.getLogger(FlightController.class);
	private static final String FUNC_TRANSFER = "transfer";

	private static final SimpleDateFormat SDF = new SimpleDateFormat(
			"yyyy/MM/dd");
	private static final SimpleDateFormat TASK_NAME_PATTTERN = new SimpleDateFormat(
			"MMM dd, yyyy", Locale.ENGLISH);

	private int offset = 10; // you want to display numbers result on screen to
								// scroll down
	private int index = 0; // you want to display numbers result on screen to
							// scroll down
	private int sortIndex = 0;

	private static List<ExchangeRate> exchangeRates;

	@Resource
	private Environment env;

	@Autowired
	private TaskService taskService;

	@Autowired
	private TaskUserIdService taskUserIdService;

	@Autowired
	private SubscribedFlightService subcribedFlightService;

	@Autowired
	private AirlineService airlineService;

	@Autowired
	private CountryService countryService;

	@Autowired
	private ExchangeRateService exchangeRateService;

	@RequestMapping(value = { "/search" }, method = RequestMethod.GET)
	public ModelAndView search(HttpSession session)
			throws JsonGenerationException, JsonMappingException, IOException {
		ModelAndView mv = new ModelAndView();
		List<Flight> sendFlight = new ArrayList<Flight>();
		List<Flight> flights = SessionHelper.getFlights(session);
		int[][] sortedIndexes = SessionHelper.getSaveSortedIndexes(session);
		for (int i = 0; i < flights.size() && i < (index + offset); i++) {
			sendFlight.add(flights.get(sortedIndexes[sortIndex][i]));
		}
		LOGGER.info("size after login: " + sendFlight.size());
		checkSubscribe(session, sendFlight);
		String jsonFlights = "";

		// Convert flight list and airlines list to json
		ObjectMapper mapper = new ObjectMapper();
		String jsonSendFlights = mapper.writeValueAsString(sendFlight);
		String jsonAirlines = mapper.writeValueAsString(SessionHelper
				.getAirlinesNameList(session));
		String jsonParams = mapper.writeValueAsString(SessionHelper
				.getSaveParameters(session));
		String jsonFilters = mapper.writeValueAsString(SessionHelper
				.getSaveParametersFilter(session));

		// combine all json
		jsonSendFlights = jsonSendFlights.replaceAll("'", " ");
		jsonFlights += jsonSendFlights;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += flights.size();
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonParams;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonFilters;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonAirlines;
		SessionHelper.setJsonController(session, jsonFlights);
		SessionHelper.setAirlinesNameList(session,
				SessionHelper.getAirlinesNameList(session));
		SessionHelper.setLoadFromFlightSearch(session, true);
		mv.addObject("params", SessionHelper.getSaveParameters(session));
		return mv;
	}

	@RequestMapping(value = { "/removeSession" }, method = RequestMethod.POST)
	@ResponseBody
	public String removeSession(HttpSession session) {
		SessionHelper.removeAirlinesNameList(session);
		SessionHelper.removeSaveParameters(session);
		SessionHelper.removeFlights(session);
		SessionHelper.removeSaveParametersFilter(session);
		SessionHelper.markSearched(session, false);
		LOGGER.info("removed sessions");
		return "success";
	}

	public boolean createNewTaskFromSearchCriteria(Parameters param,
			HttpSession session) {
		TaskDTO taskDTO = new TaskDTO();
		Task task = new Task();
		TaskUserId taskUserId = new TaskUserId();

		taskDTO.setFromAirport(param.getFromLocation());
		taskDTO.setToAirport(param.getToLocation());
		taskDTO.setUserId(SessionHelper.getUserId(session));
		taskDTO.setCurrencyCode(CONSTANTS.DEFAULT_CURRENCY);
		taskDTO.setMaxPrice(CONSTANTS.DEFAULT_MAX_PRICE);
		taskDTO.setMaxStops(param.getNumStop());
		taskDTO.setFlexibleDays(CONSTANTS.DEFAULT_FLEXIBLEDAYS);
		taskDTO.setCabinClass(cabinClasses[Integer.parseInt(param
				.getCabinClass())]);
		if (!("unlike").equals(param.getAirlinesFilterMode())) {
			taskDTO.setAirlines(param.getAirlines());
		} else {
			taskDTO.setAirlines("");
		}
		// remove , character at the end
		String fromLocation = (param.getFromLocation() + "  ").replace(",  ",
				"").trim();
		String toLocation = (param.getToLocation() + "  ").replace(",  ", "")
				.trim();

		int pos1 = fromLocation.indexOf("-");
		int pos2 = toLocation.indexOf("-");
		int pos3 = fromLocation.lastIndexOf(",");
		int pos4 = toLocation.lastIndexOf(",");
		taskDTO.setFromCode(fromLocation.substring(0, pos1 - 1).trim());
		taskDTO.setToCode(toLocation.substring(0, pos2 - 1).trim());
		if (pos3 == -1) {
			taskDTO.setFromAirport(fromLocation.substring(pos1 + 2).trim());
			taskDTO.setToAirport(toLocation.substring(pos2 + 2).trim());
			taskDTO.setFromCountry(null);
			taskDTO.setToCountry(null);
		} else {
			taskDTO.setFromAirport(fromLocation.substring(pos1 + 2, pos3)
					.trim());
			taskDTO.setToAirport(toLocation.substring(pos2 + 2, pos4).trim());
			taskDTO.setFromCountry(fromLocation.substring(pos3 + 2).trim());
			taskDTO.setToCountry(toLocation.substring(pos4 + 2).trim());
		}
		// set departure date
		try {
			Date departDate = SDF.parse(param.getOutBound().trim());
			taskDTO.setDepartDate(departDate);
		} catch (ParseException e) {
			LOGGER.info("Can't set Depart Date: ", e);
		}
		if (CONSTANTS.ROUND_TRIP.equals(param.getTypeOfTrip())) {
			taskDTO.setTypeOfTrip(param.getTypeOfTrip());
			try {
				Date returnDate = SDF.parse(param.getInBound().trim());
				taskDTO.setReturnDate(returnDate);
			} catch (ParseException e) {
				LOGGER.info("Can't set Return Date: ", e);
			}
		} else {
			taskDTO.setTypeOfTrip(CONSTANTS.ONE_WAY);
		}

		// set name for task
		String name = "New Task: " + taskDTO.getFromCode() + "-"
				+ taskDTO.getToCode();
		try {
			name += "&nbsp;&nbsp;&nbsp;"
					+ TASK_NAME_PATTTERN.format(SDF.parse(param.getOutBound()
							.trim()));
		} catch (ParseException e) {
			LOGGER.info("convert depart date exception: " + e);
		}
		taskDTO.setName(name);
		taskDTO.setTaskId(TaskDTO.makeTaskId(taskDTO));

		task = taskDTO.getTask(taskDTO);
		taskUserId = taskDTO.getTaskUserId(taskDTO);

		// check exits in DB
		TaskUserId temp = taskUserIdService.findByUserIdAndTaskId(
				SessionHelper.getUserId(session), taskUserId.getTaskId());
		if (temp == null) {
			LOGGER.info("Task ID: " + task.getTaskId() + " User ID: " + SessionHelper.getUserId(session));
			if (taskService.findByTaskId(task.getTaskId()) == null) {
				taskService.create(task);
				taskUserIdService.create(taskUserId);
			}
			else {
				taskUserIdService.create(taskUserId);
			}
			
			return true;
		}

		return false;
	}

	@RequestMapping(value = { "/search" }, method = RequestMethod.POST)
	@ResponseBody
	public String getFlightFromSearch(@ModelAttribute @Valid Parameters param,
			BindingResult result, @ModelAttribute ParametersFilter paramFilter,
			RedirectAttributes redirectAttribute, HttpSession session)
			throws IOException, Exception {

		LOGGER.info("getFlightFromSearch");
		String quickTaskFlag = "createQuickTask_";
		String jsonFlights = "";
		LOGGER.info("get flight from search:  " + param.getFunctionType());
		String error = "nonerror";
		try {
			ParamValidator paramValidator = new ParamValidator();
			error = paramValidator.validate(param);
		} catch (Exception e) {
			error = "exception";
		}

		if (!"nonerror".equals(error)) {
			LOGGER.info("in err: " + error);
			jsonFlights += error;
			return jsonFlights;
		}

		if (FUNC_TRANSFER.equals(param.getFunctionType())) {
			if (!SessionHelper.isLoggedIn(session)) {
				// jsonFlights=SessionHelper.getJsonController(session);
				SessionHelper.setSaveParameters(session, param);
				SessionHelper.setTransferFlag(session, true);
				jsonFlights = "NOLOGIN";
				return jsonFlights;
			}
			boolean isSuccess = createNewTaskFromSearchCriteria(param, session);
			jsonFlights = quickTaskFlag + isSuccess;
			LOGGER.info(jsonFlights);
			return jsonFlights;
		}

		LOGGER.info("getFlightFromSearch - search processing area IN");
		// reset offset and index when make a new search
		index = 0;
		offset = 10;
		if ("".equals(param.getAirlinesFilterMode()))
			param.setAirlinesFilterMode("like");

		List<Flight> flights;

		if (result.hasErrors()) {
			ModelAndView mav = new ModelAndView("flight/search");
			mav.addObject("parameters", param);
			return "";
		}
		if (param.getCabinClass().equals(CONSTANTS.CABIN_ALL_2)) {
			param.setCabinClass(CONSTANTS.CABIN_ECONOMY_0);
			flights = FlightRequesterService.getInstance().sendRequest(param);
			param.setCabinClass(CONSTANTS.CABIN_BUSSINESS_1);
			List<Flight> flightstemp = FlightRequesterService.getInstance()
					.sendRequest(param);
			param.setCabinClass(CONSTANTS.CABIN_ALL_2);
			for (int j = 0; j < flightstemp.size(); j++) {
				flights.add(flightstemp.get(j));
			}
		} else {
			flights = FlightRequesterService.getInstance().sendRequest(param);
		}

		LOGGER.info("getFlightFromSearch - search result: " + flights.size());
		FlightFactoryService flightFactoryService = new FlightFactoryService();
		flightFactoryService.setParameter(param);
		List<AirlinesFilter> airlinesFilter = new ArrayList<AirlinesFilter>();
		// Set id for each flight
		// get all airlines name and amount
		int idx = 0;
		for (Flight flight : flights) {
			flight.setId(idx);
			flightFactoryService
					.getAllAirlinesAndAmount(flight, airlinesFilter);
			idx++;
		}

		flightFactoryService.sortAirlinesByName(airlinesFilter);

		int[][] sortedIndexes = new int[3][];
		for (int i = 0; i < 3; i++) {
			sortedIndexes[i] = new int[idx];
			for (int j = 0; j < idx; j++) {
				sortedIndexes[i][j] = j;
			}
		}

		// [0 = Price] [1 = Airline] [2 = no of stops]
		int tmp;
		for (int i = 0; i < idx; i++) {
			for (int j = i; j < idx; j++) {
				// Price
				if (Float.parseFloat(flights.get(sortedIndexes[0][i])
						.getPrice().getAmount()) > Float.parseFloat(flights
						.get(sortedIndexes[0][j]).getPrice().getAmount())) {

					tmp = sortedIndexes[0][i];
					sortedIndexes[0][i] = sortedIndexes[0][j];
					sortedIndexes[0][j] = tmp;
				}

				// airline
				String outbound1 = flights.get(sortedIndexes[1][i])
						.getOutBound().getDepartAirports().get(0).getAirline()
						.getName();
				String outbound2 = flights.get(sortedIndexes[1][j])
						.getOutBound().getDepartAirports().get(0).getAirline()
						.getName();

				String inbound1 = "";
				String inbound2 = "";

				if (flights.get(sortedIndexes[1][i]).getInBound() != null) {
					inbound1 = flights.get(sortedIndexes[1][i]).getInBound()
							.getDepartAirports().get(0).getAirline().getName();
				}

				if (flights.get(sortedIndexes[1][j]).getInBound() != null) {
					inbound2 = flights.get(sortedIndexes[1][j]).getInBound()
							.getDepartAirports().get(0).getAirline().getName();
				}

				if (outbound1.compareTo(outbound2) > 0
						|| (outbound1.equals(outbound2) && inbound1
								.compareTo(inbound2) > 0)) {
					tmp = sortedIndexes[1][i];
					sortedIndexes[1][i] = sortedIndexes[1][j];
					sortedIndexes[1][j] = tmp;
				}

				int total1 = Integer.parseInt(flights.get(sortedIndexes[2][i])
						.getOutBound().getNumberStops());
				int total2 = Integer.parseInt(flights.get(sortedIndexes[2][j])
						.getOutBound().getNumberStops());

				if (flights.get(sortedIndexes[2][i]).getInBound() != null) {
					total1 += Integer.parseInt(flights.get(sortedIndexes[2][i])
							.getInBound().getNumberStops());
				}

				if (flights.get(sortedIndexes[2][j]).getInBound() != null) {
					total2 += Integer.parseInt(flights.get(sortedIndexes[2][j])
							.getInBound().getNumberStops());
				}

				if (total1 > total2) {
					tmp = sortedIndexes[2][i];
					sortedIndexes[2][i] = sortedIndexes[2][j];
					sortedIndexes[2][j] = tmp;
				}
			}
		}
		List<String> airlines = Arrays.asList(paramFilter.getAirlinesFilter()
				.split(CONSTANTS.TOKEN_DELIMITER));
		for (int i = 0; i < airlinesFilter.size(); i++) {
			if (airlines.contains(airlinesFilter.get(i).getName())) {
				airlinesFilter.get(i).setCheck(false);
			}
		}

		int indexFlight = 0;
		List<Flight> sendFlight = new ArrayList<>();
		for (int i = indexFlight; i < flights.size()
				&& i < (indexFlight + offset); i++) {
			sendFlight.add(flights.get(sortedIndexes[0][i]));
		}

		checkSubscribe(session, sendFlight);
		// Convert flight list to json
		ObjectMapper mapper = new ObjectMapper();
		String jsonSendFlights = mapper.writeValueAsString(sendFlight);
		String jsonAirlines = mapper.writeValueAsString(airlinesFilter);
		String jsonParams = mapper.writeValueAsString(param);
		String jsonFilters = mapper.writeValueAsString(paramFilter);
		// sort airlines by name

		// combine all json
		jsonFlights += jsonSendFlights;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += flights.size();
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonParams;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonFilters;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonAirlines;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;

		LOGGER.info("Size of final flights list: " + flights.size());
		IOUtil.logToFile("getFlightFromSearch - Size of final flights list: "
				+ flights.size());
		// send to front-end
		SessionHelper.setJsonController(session, jsonFlights);
		SessionHelper.setAirlinesNameList(session, jsonAirlines);
		SessionHelper.setFlights(session, flights);
		SessionHelper.setSaveSortedIndexes(session, sortedIndexes);
		SessionHelper.setSaveParameters(session, param);
		SessionHelper.setSaveParametersFilter(session, paramFilter);
		SessionHelper.markSearched(session, true);
		if (!SessionHelper.isLoggedIn(session)) {
			String currencyCode = getSupportedCurrencyCode(param
					.getFromLocation());
			SessionHelper.setCurrencyCode(session, currencyCode);
			jsonFlights += currencyCode;
		}
		IOUtil.logToFile("getFlightFromSearch - jsonFlights: " + jsonFlights);
		SessionHelper.setJSONFlights(session, jsonFlights);
		return jsonFlights;
	}

	private List<ExchangeRate> getListSupportedCurrencies() {
		if (exchangeRates == null) {
			LOGGER.info("re create list currency");
			exchangeRates = exchangeRateService.findAll();
		}
		return exchangeRates;
	}

	private String getSupportedCurrencyCode(String fromLocation) {
		String currencyCode = getCurrencyCodeFromDepartureAirport(fromLocation);
		String[] currencyCodes = currencyCode.split("-");
		int position;
		try {
			List<ExchangeRate> currencies = getListSupportedCurrencies();
			for (ExchangeRate currency : currencies) {
				position = Utils.hasContain(currencyCodes, currency.getCode());
				if (position == -1) {
					continue;
				} else
					return currencyCodes[position];
			}

		} catch (Exception e) {
			LOGGER.info("get currency code exception", e);
		}
		return CONSTANTS.DEFAULT_CURRENCY;
	}

	private String getCurrencyCodeFromDepartureAirport(String fromLocation) {
		// String
		// currencyCode=getCurrencyCodeFromCountryCode(param.getFromLocation().trim().substring(param.getFromLocation().trim().length()-2));
		LOGGER.info("getCurrencyCodeFromDepartureAirport");
		LOGGER.info("fromLocation: " + fromLocation);
		String[] country = fromLocation.split(",");
		String currencyCode;
		if (country[1].trim().length() == 2) {// the information contains
												// country code
			currencyCode = countryService
					.getCurrencyCodeFromCountryCode(country[1].trim());
		} else {// the informationo contains country name
			currencyCode = countryService
					.getCurrencyCodeFromCountryName(country[1].trim());
		}

		LOGGER.info("currency code: " + currencyCode);
		return currencyCode;

	}

	@RequestMapping(value = { "/searchmockdb" }, method = RequestMethod.GET)
	public ModelAndView searchmockdb() {
		return new ModelAndView("demo/searchMockDB");
	}

	@RequestMapping(value = { "/searchmockdb" }, method = RequestMethod.POST)
	public ModelAndView getFlightFromMockDBSearch(
			@ModelAttribute @Valid Parameters param, BindingResult result,
			RedirectAttributes redirectAttribute,
			HttpServletRequest httpRequest, HttpSession session)
			throws IOException, Exception {
		ModelAndView mav = new ModelAndView("redirect:/searchmockdb.html");
		if (result.hasErrors()) {
			return mav;
		}

		List<Flight> flights = FlightRequesterService.getInstance()
				.sendRequestMockDB(param, httpRequest);

		LOGGER.info("Size of final flights list: " + flights.size());
		mav = new ModelAndView("search_result_mockDB");
		mav.addObject("flights", flights);

		SessionHelper.setFlights(session, flights);
		SessionHelper.setSaveParameters(session, param);
		return mav;
	}

	@RequestMapping(value = "/subscribe", method = RequestMethod.POST)
	@ResponseBody
	public ModelAndView subcribeFlight(@RequestBody String id,
			HttpSession session, RedirectAttributes redirectAttribute)
			throws JsonGenerationException, JsonMappingException, IOException {
		JSONObject object = new JSONObject(id);
		String flightId = object.getString("flightId");
		JSONArray airlines = object.getJSONArray("carriers");

		ParametersFilter paramFilter = new ParametersFilter();
		String airlineNames = airlines.toString();
		LOGGER.info("airlineNames: " + airlineNames);
		List<Flight> flights = SessionHelper.getFlights(session);
		Parameters param = SessionHelper.getSaveParameters(session);

		paramFilter.setDepartMin(object.getString("departMin"));
		paramFilter.setDepartMax(object.getString("departMax"));
		paramFilter.setReturnMin(object.getString("returnMin"));
		paramFilter.setReturnMax(object.getString("returnMax"));
		paramFilter.setDurationMin(object.getString("durationMin"));
		paramFilter.setDurationMax(object.getString("durationMax"));
		paramFilter.setNumStopFilter(object.getString("stopsFilter"));

		if (SessionHelper.isLoggedIn(session)) {
			int userId = SessionHelper.getUserId(session);
			Flight flight = Flight.getFlightByFlightId(flights, flightId);
			SubscribedFlight subcribedFlight = subcribedFlightService
					.formatFlight(userId, flight, param);
			SessionHelper.setSaveParametersFilter(session, paramFilter);
			return retainingValue(subcribedFlight, userId, flights, param,
					paramFilter, airlineNames, session, redirectAttribute);
		} else {
			SessionHelper.setTempFlight(session,
					Flight.getFlightByFlightId(flights, flightId));
			SessionHelper.setAirlinesNameList(session, airlineNames);
			SessionHelper.setSaveParametersFilter(session, paramFilter);
			return new ModelAndView("redirect:/account/login.html");
		}
	}

	public ModelAndView retainingValue(SubscribedFlight subcribedFlight,
			int userId, List<Flight> flights, Parameters param,
			ParametersFilter paramFilter, String airlineNames,
			HttpSession session, RedirectAttributes redirectAttribute)
			throws JsonGenerationException, JsonMappingException, IOException {
		ModelAndView mav = new ModelAndView("redirect:/flight/search.html");
		if (subcribedFlight != null) {
			if (subcribedFlightService.alreadyAddedSubscribeFlight(
					subcribedFlight, userId)) {
				redirectAttribute.addFlashAttribute("quickTaskResult",
						CONSTANTS.ALREADYSUBSCRIBED);
			} else {
				subcribedFlightService.create(subcribedFlight, userId);
				redirectAttribute.addFlashAttribute("quickTaskResult",
						CONSTANTS.NOTSUBSCRIBED);
			}
		} else {
			// subscribed flight is null, it mean that the user click transfer
			// button
			redirectAttribute.addFlashAttribute("quickTaskResult",
					SessionHelper.isCreateQuickTaskResult(session));
			LOGGER.info("create quick task result: "
					+ SessionHelper.isCreateQuickTaskResult(session));
			SessionHelper.removeCreateQuickTaskResult(session);

		}
		List<Flight> sendFlight = new ArrayList<Flight>();
		int[][] sortedIndexes = SessionHelper.getSaveSortedIndexes(session);
		for (int i = 0; i < flights.size() && i < (index + offset); i++) {
			sendFlight.add(flights.get(sortedIndexes[sortIndex][i]));
		}
		LOGGER.info("size after login: " + sendFlight.size());
		checkSubscribe(session, sendFlight);
		String jsonFlights = "";

		// Convert flight list and airlines list to json
		ObjectMapper mapper = new ObjectMapper();
		String jsonSendFlights = mapper.writeValueAsString(sendFlight);
		String jsonAirlines = mapper.writeValueAsString(airlineNames);
		String jsonParams = mapper.writeValueAsString(param);
		String jsonFilters = mapper.writeValueAsString(paramFilter);

		// combine all json
		// replace all ' to space to prevent err at javascript
		jsonSendFlights = jsonSendFlights.replaceAll("'", " ");
		jsonFlights += jsonSendFlights;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += flights.size();
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonParams;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonFilters;
		jsonFlights += CONSTANTS.SEARCH_DELIMITER;
		jsonFlights += jsonAirlines;
		SessionHelper.setJsonController(session, jsonFlights);
		SessionHelper.setAirlinesNameList(session, airlineNames);

		return mav;

	}

	@RequestMapping(value = "/index", method = RequestMethod.GET)
	public ModelAndView returnHome() {
		return new ModelAndView("index");
	}

	private void checkSubscribe(HttpSession session, List<Flight> flights) {
		boolean isLoggedIn = false;
		if (SessionHelper.isLoggedIn(session)) {
			isLoggedIn = true;
		}

		int userId = SessionHelper.getUserId(session);
		List<SubscribedFlight> subcribedFlights = subcribedFlightService
				.findByUserId(userId);
		for (Flight flight : flights) {
			if (isLoggedIn) {
				flight.setCheckSubscribe(0);
				for (int j = 0; j < subcribedFlights.size(); j++) {
					if (subcribedFlights.get(j).getFlightId()
							.equals(flight.getFlightID())) {
						flight.setCheckSubscribe(1);
						break;
					}
				}
			} else {
				flight.setCheckSubscribe(-1);
			}
		}
	}

	@RequestMapping(value = "/scrolldown", method = RequestMethod.POST)
	@ResponseBody
	public String scrolldown(@RequestBody String json, HttpSession session)
			throws JsonGenerationException, JsonMappingException, IOException {

		JSONObject object = new JSONObject(json);
		index = Integer.valueOf(object.getString("index"));
		offset = Integer.valueOf(object.getString("offset"));
		sortIndex = Integer.valueOf(object.getString("sortIndex"));

		LOGGER.info("index: " + index);
		LOGGER.info("offset: " + offset);

		List<Flight> flights = SessionHelper.getFlights(session);
		List<Flight> sendFlight = new ArrayList<>();

		int[][] sortedIndexes = SessionHelper.getSaveSortedIndexes(session);
		for (int i = index; i < flights.size() && i < (index + offset); i++) {
			// LOGGER.info(flights.size());
			sendFlight.add(flights.get(sortedIndexes[sortIndex][i]));
		}

		checkSubscribe(session, sendFlight);
		// Convert flight list to json
		ObjectMapper mapper = new ObjectMapper();
		String jsonFlights = mapper.writeValueAsString(sendFlight);
		LOGGER.info("scroll down exit ");
		return jsonFlights;
	}

	@RequestMapping(value = "/updateParameterFilter", method = RequestMethod.POST)
	@ResponseBody
	public String updateParameterFilter(@RequestBody String json,
			HttpSession session) throws JsonGenerationException,
			JsonMappingException, IOException {
		LOGGER.info("updateParameterFilter:" + json);
		JSONObject object = new JSONObject(json);
		String type = object.getString("type");
		String value1 = object.getString("value1");
		String value2 = object.getString("value2");
		LOGGER.info("type: " + type + " value1: " + value1 + " value2: "
				+ value2);
		updateParameter(session, type, value1, value2);
		return "success";
	}

	@RequestMapping(value = "/updateCurrencyCode", method = RequestMethod.POST)
	@ResponseBody
	public String updateCurrencyCode(@RequestBody String json,
			HttpSession session) throws JsonGenerationException,
			JsonMappingException, IOException {
		LOGGER.info("updateCurrencyCode:" + json);
		SessionHelper.setCurrencyCode(session, json);
		return "success";
	}

	private void updateParameter(HttpSession session, String type,
			String value1, String value2) {
		ParametersFilter paraFilter = SessionHelper
				.getSaveParametersFilter(session);
		switch (type) {
		case "1":
			paraFilter.setNumStopFilter(value1);
			break;
		case "2":
			paraFilter.setDepartMin(value1);
			paraFilter.setDepartMax(value2);
			break;
		case "3":
			paraFilter.setReturnMin(value1);
			paraFilter.setReturnMax(value2);
			break;
		case "4":
			paraFilter.setDurationMin(value1);
			paraFilter.setDurationMax(value2);
			break;
		case "5":
			String airlines = value1;
			SessionHelper.setAirlinesNameList(session, airlines);
			break;
		}
		SessionHelper.setSaveParametersFilter(session, paraFilter);
	}

}
